package com.rest.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Verb;
import org.scribe.utils.StreamUtils;

import com.rest.model.HttpMethod;
import com.rest.model.Parameter;
import com.rest.model.RestRequest;
import com.rest.model.RestResponse;

public class RestScribeRequest implements RestRequest {
	private static final int DEFAULT_BUFFER_SIZE = 1024;

	private final OAuthRequest request;

	private final HttpMethod method;
	private final List<Parameter<String>> queryParams;
	private final List<Parameter<Object>> formParams;

	RestScribeRequest(HttpMethod method, String url, Map<String, String> headers, List<Parameter<String>> queryParams,
			List<Parameter<Object>> formParams) {

		if (method == null || url == null) {
			throw new IllegalArgumentException("Method and url cannot be null");
		}
		this.method = method;
		this.queryParams = Collections.unmodifiableList(queryParams);
		this.formParams = Collections.unmodifiableList(formParams);

		Verb verb = Verb.valueOf(method.toString());
		this.request = new OAuthRequest(verb, url);

		for (Entry<String, String> entry : headers.entrySet()) {
			request.addHeader(entry.getKey(), entry.getValue());
		}
		for (Parameter<String> param : queryParams) {
			request.addQuerystringParameter(param.getName(), param.getValue());
		}
		for (Parameter<Object> param : formParams) {
			if (param.getValue() != null) {
				String value = String.valueOf(param.getValue());
				request.addBodyParameter(param.getName(), value);
			}
		}
	}

	@Override
	public RestResponse send() {

		RequestLogger.logRequest(this);
		Response response = request.send();

		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			if (response.getStream() != null) {
				byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
				int len;
				while ((len = response.getStream().read(buffer)) > -1) {
					baos.write(buffer, 0, len);
				}
				baos.flush();
			}
			InputStream stream = new ByteArrayInputStream(baos.toByteArray());
			String body = StreamUtils.getStreamContents(stream);

			InputStream copiedStream = new ByteArrayInputStream(baos.toByteArray());

			RestResponse apiResponse = new RestResponseImpl(this, response.getCode(), body, copiedStream);
			RequestLogger.logResponse(apiResponse);
			return apiResponse;

		} catch (IOException e) {
			throw new IllegalStateException("Couldn't make copy of input stream", e);
		}
	}

	@Override
	public HttpMethod getMethod() {
		return method;
	}

	@Override
	public String getUrl() {
		return request.getUrl();
	}

	@Override
	public String getHeader(String name) {
		if (!request.getHeaders().containsKey(name)) {
			throw new IllegalArgumentException(String.format("Header '%s' doesn't exist in request", name));
		}
		return request.getHeaders().get(name);
	}

	@Override
	public Map<String, String> getHeaders() {
		return request.getHeaders();
	}

	@Override
	public List<Parameter<String>> getQueryParams() {
		return queryParams;
	}

	@Override
	public List<Parameter<Object>> getFormParams() {
		return formParams;
	}
}
